using System.Collections;
using System.Collections.Generic;
using System;
using Slaks.Graphs.GraphBuilder;

namespace Slaks.Graphs
{
	public class Graph
	{
		protected Id2VertexMap m_id2VertexMapper;
		protected VertexList m_vertexList;
		private string m_name;
		private string m_category;
		private bool m_isCategorySet;
		public BuilderType GraphBuilderType {get;set;}
		public Graph()
		{
			this.Init();            
		}

		public Graph(string name)
		{
			m_name = name;
            this.Init();            
		}

		private void Init()
		{
			m_id2VertexMapper = new Id2VertexMap();
			m_vertexList = new VertexList();
			m_category = "";
			m_isCategorySet = false;
		}

		/// <summary>
		/// Get or set the name of the graph
		/// </summary>
		public string Name
		{
			get { return m_name; }
			set { m_name = value; }
		}


		/// <summary>
		/// Get or set the category of the graph
		/// </summary>
		public string Category
		{
			get { return m_category; }
			set
			{
				m_category = value;
				m_isCategorySet = true;
			}
		}

		/// <summary>
		/// Find out whether the category of the graph was set or not
		/// </summary>
		/// <returns></returns>
		public bool IsCategorySet()
		{
			return m_isCategorySet;
		}

		/// <summary>
		/// Get the inner map which maps id to vertex <br>
		/// Do not use for changing
		/// </summary>
		/// <returns></returns>
		public Id2VertexMap GetId2VertexMap()
		{
			return m_id2VertexMapper;
		}

		/// <summary>
		/// Get the inner list which holds the vertices <br>
		/// Do not use for changing
		/// </summary>
		/// <returns></returns>
		public VertexList GetVertexList()
		{
			return m_vertexList;
		}

		/// <summary>
		/// Get the number of vertices in the graph
		/// </summary>
		/// <returns></returns>
		public long GetVertexCount()
		{
			return m_vertexList.Count;
		}


		/// <summary>
		/// Get the vertex overall frequency in the graph
		/// </summary>
		/// <returns></returns>
		public double GetVertexFrequency()
		{
			double vertexFrequency = 0;
			foreach (Vertex vertex in m_vertexList)
			{
				vertexFrequency += vertex.Frequency;
			}
			return vertexFrequency;
		}

		/// <summary>
		/// Get the number of edges in the graph
		/// </summary>
		/// <returns>number of edges</returns>
		public long GetEdgeCount()
		{
			long edgeCount = 0;
			foreach (Vertex vertex in m_vertexList)
			{
				edgeCount += vertex.CountEdges();
			}
			return edgeCount;
		}


		/// <summary>
		/// Get the frequency of all edges in the graph
		/// </summary>
		/// <returns></returns>
		public double GetEdgeFrequency()
		{
			double edgeFrequency = 0;
			foreach (Vertex vertex in m_vertexList)
			{
				foreach (EdgeList edgeList in vertex.GetSection2EdgeMap().Values)
				{
					foreach (Edge edge in edgeList)
					{
						edgeFrequency += edge.GetFrequency();
					}
				}
			}
			return edgeFrequency;
		}

		/// <summary>
		/// Get the vertex by its id
		/// </summary>
		/// <param name="id">vertex id</param>
		/// <returns>vertex if exists and null otherwise</returns>
		public Vertex GetVertex(long id)
		{
			if (m_id2VertexMapper.Contains(id)) return m_id2VertexMapper[id];
			return null;
		}

		/// <summary>
		/// Add vertex to the graph. If the vertex with the same id already exists then the vertex won't be added and false returns
		/// </summary>
		/// <param name="vertex"></param>
		/// <returns>true if vertex was added and false otherwise</returns>
		public bool AddVertex(Vertex vertex)
		{
			if (m_id2VertexMapper.Contains(vertex.GetId())) return false;
			m_id2VertexMapper.Add(vertex.GetId(), vertex);
			m_vertexList.Add(vertex);
			return true;
		}

        /// <summary>
        /// Get the common graph between two graphs (Max common)
        /// </summary>
        /// <param name="one"></param>
        /// <param name="two"></param>
        /// <returns></returns>
        public virtual Graph GetMCS(Graph two)
        {
            this.SortVertices();
            two.SortVertices();

            Graph result = new Graph();

            VertexList oneList = this.GetVertexList();
            VertexList twoList = two.GetVertexList();

            int compare;

            #region find vertices

            //find intersections
            int i = 0, j = 0;
            while (i < oneList.Count && j < twoList.Count)
            {
                compare = oneList[i].CompareTo(twoList[j]);
                if (compare < 0) i++;
                else if (compare == 0)
                {
                    result.CreateAddVertex(oneList[i].GetId());
                    i++;
                    j++;
                }
                else j++;
            }

            #endregion

            #region find edges

            VertexList resultList = result.GetVertexList();
            if (resultList.Count <= 1) return result;

            Vertex vertexOne;
            Vertex vertexTwo;

            Vertex vertexOneNext;
            Vertex vertexTwoNext;

            long id;
            long nextId;
            IDictionary sectionsOne, sectionsTwo;
            for (i = 0; i < resultList.Count; i++)
            {
                id = resultList[i].GetId();
                vertexOne = this.GetVertex(id);
                vertexTwo = two.GetVertex(id);

                for (j = i; j < resultList.Count; j++)
                {
                    nextId = resultList[j].GetId();
                    sectionsOne = vertexOne.GetSectionsIfConnected(nextId);
                    sectionsTwo = vertexTwo.GetSectionsIfConnected(nextId);

                    ICollection sectionsOneKeys = sectionsOne.Keys;

                    foreach (string section in sectionsOneKeys)
                    {
                        if (sectionsTwo.Contains(section))
                        {
                            resultList[i].AddEdge(resultList[j], section);
                        }
                    }

                    if (id == nextId) continue;
                    vertexOneNext = this.GetVertex(nextId);
                    vertexTwoNext = two.GetVertex(nextId);

                    sectionsOne = vertexOneNext.GetSectionsIfConnected(id);
                    sectionsTwo = vertexTwoNext.GetSectionsIfConnected(id);

                    sectionsOneKeys = sectionsOne.Keys;
                    foreach (string section in sectionsOneKeys)
                    {
                        if (sectionsTwo.Contains(section))
                        {
                            resultList[j].AddEdge(resultList[i], section);
                        }
                    }

                } //end of for
            }

            #endregion

            return result;
        }

		/// <summary>
		/// Create the vertex with specified id. <br>
		/// If the vertex already exists then return this vertex otherwise create new, add to graph and return  the vertex
		/// </summary>
		/// <param name="id"></param>
		/// <returns></returns>
		public Vertex CreateAddVertex(long id)
		{
			Vertex vertex = GetVertex(id);
			if (vertex != null) return vertex;
			vertex = new Vertex(id);
			m_id2VertexMapper.Add(vertex.GetId(), vertex);
			m_vertexList.Add(vertex);
			return vertex;
		}

        public long GetGraphSizeVertexEdgeCount
        {
            get 
            {
                return (GetVertexCount() + GetEdgeCount());
            }
        }

        public double GetGraphSizeVertexEdgeFrequency
        {
            get
            {
                return (GetVertexFrequency() + GetEdgeFrequency());
            }
        }

		#region Sorting

		/// <summary>
		/// Sort by id
		/// </summary>
		public void SortVertices()
		{
			m_vertexList.Sort();
		}

		/// <summary>
		/// Sort by custom comparer
		/// </summary>
		/// <param name="comparer"></param>
		public void SortVertices(IComparer comparer)
		{
			m_vertexList.Sort(comparer);
		}

		#endregion
	}
}